[源代码]- Test 96 变换动画
这里分享一个变换算法。原理简单,效果有趣。你可以绘制任何内容,然后对图形进行形态转换。
实现原理
包含两个部分
1.运动算法
2.关联坐标点
运动算法
有尝试用程序写过运动效果的想必非常熟悉这个写法。
(Processing 代码)
PVector A,B;
void setup(){
size(500,500);
A = new PVector(width/2,height/2);
}
void draw(){
background(0);
B = new PVector(mouseX,mouseY);
A.x = A.x + (B.x - A.x) * 0.05;
A.y = A.y + (B.y - A.y) * 0.05;
fill(255);
ellipse(A.x,A.y,10,10);
}
点A往点B的方向不断累加,并按比例地不断逼近点 B,则可实现由快至慢的运动效果。你也可以用其他的运动算法来决定点 A 的运动形式。匀速,变速,直线运动,曲线运动。
关联坐标点
一般用数组或是vector来记录点坐标,所以可以将两个不同的绘画轨迹分成两组来保存。外面再加一个 for 循环做遍历,就可以将上面的运动效果,应用到每一个点上。但问题在于,每组点的数据量,往往是不一致的。画面的内容越多,点的坐标数就越多。因此无法完全一一对应。
这里有一个巧妙的解决方法。
(关键代码)
if(pic1.length < pic2.length){
int addNum = pic2.length - pic1.length;
for(int i = 0;i < addNum;i++){
pic1 = (PVector [])append(pic1,pic1[int(random(pic1.length-1))]);
}
}
if(pic1.length > pic2.length){
int addNum = pic1.length - pic2.length;
for(int i = 0;i < addNum;i++){
pic2 = (PVector [])append(pic2,pic2[int(random(pic2.length-1))]);
}
}
只要在点的数量相对少的一边,通过随机的方式,补充点坐标,就可以使两边的点一一对应起来。这种做法不会破坏点数较少一边的造型,相当于某些点有了重叠的分身。它会在运动的过程中,分裂成若干个点。又或是若干个点,汇聚成一个点。
Processing完整代码:
PVector []pic1;
PVector []pic2;
int curPic;
boolean running;
void setup(){
size(500,500);
pic1 = new PVector[0];
pic2 = new PVector[0];
curPic = 1;
}
void draw(){
background(0);
if(curPic == 1){
for(int i = 0;i < pic1.length;i++){
noStroke();
fill(255);
ellipse(pic1[i].x,pic1[i].y,5,5);
}
}
if(curPic == 2){
for(int i = 0;i < pic2.length;i++){
noStroke();
fill(255);
ellipse(pic2[i].x,pic2[i].y,5,5);
}
}
if(running && pic1.length!= 0 && pic2.length!= 0){
for(int i = 0;i < pic2.length;i++){
pic2[i].x += (pic1[i].x - pic2[i].x) * 0.05;
pic2[i].y += (pic1[i].y - pic2[i].y) * 0.05;
}
}
}
void keyPressed(){
if(key == '1'){
curPic = 1;
}
if(key == '2'){
curPic = 2;
}
if(key == 'r'){
if(pic1.length < pic2.length){
int addNum = pic2.length - pic1.length;
for(int i = 0;i < addNum;i++){
pic1 = (PVector [])append(pic1,pic1[int(random(pic1.length-1))]);
}
}
if(pic1.length > pic2.length){
int addNum = pic1.length - pic2.length;
for(int i = 0;i < addNum;i++){
pic2 = (PVector [])append(pic2,pic2[int(random(pic2.length-1))]);
}
}
running = true;
}
}
void mouseDragged(){
if(curPic == 1){
pic1 = (PVector [])append(pic1,new PVector(mouseX,mouseY));
}
if(curPic == 2){
pic2 = (PVector [])append(pic2,new PVector(mouseX,mouseY));
}
if(curPic == 'r'){
running = true;
}
}
上面的代码可以完整地体现变化的过程。
数字键 1 ,2 切换画布
数字键 1 对应绘制图形1
数字键 2 对应绘制图形2
‘r’键:开始变换动画,将图形 2 变成图形 1.
为了让代码结构更清晰,做了一定程度的简化。以上的转换过程是不可逆的,如果你希望可以自由来回切换,则需要创建一个数组 temp 来作为中间变量,以保存变换之前的点坐标。
其它形式
这种变换可以有多种变式。
除了二维的点,三维的点同样可以采取同样的方式来进行变换。
你只要导入模型,获取模型的顶点数据。
图片的转换也是同理,由于图片尺寸本身是规整的,前后像素点的数量亦一致,所以只需建立点与点之间的对应关系。
这里将前后两张图片进行了明度排序,图片A最亮的像素点,对应用图片B最亮的像素点,由亮到暗往下排序。由于这个变换过程并不改变原始图片像素的颜色,所以前后图片灰度像素的层次分布越相似,最终还原的效果偏差会越少。
坐标点也可以替换成图片素材,可以依此制作一个围棋棋形变化的动画。
因为是用棋子去表现,如果某些棋子是凭空出现,会显得比较奇怪。所以在排列设计棋形的时候,就刻意用数量相同的棋子(数量都是51)。
END
“阅读原文” 可获取 OF 源码